// --------------------------------------------------------------------------------------------------------------------
// <copyright file="InstrumentStaticData.cs" company="Open Trader">
//   Copyright (c) David Denis (david.denis@systemathics.com)
// </copyright>
// <summary>
//   |  Open Trader - The Open Source Systematic Trading Platform
//   |
//   |  This program is free software: you can redistribute it and/or modify
//   |  it under the terms of the GNU General Public License as published by
//   |  the Free Software Foundation, either version 2 of the License, or
//   |  (at your option) any later version.
//   |
//   |  This program is distributed in the hope that it will be useful,
//   |  but WITHOUT ANY WARRANTY; without even the implied warranty of
//   |  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//   |  GNU General Public License for more details.
//   |
//   |  You should have received a copy of the GNU General Public License
//   |  along with this program.  If not, see http://www.gnu.org/licenses
//   |
//   |  Up to date informations about Open Trader can be found at :
//   |    http://opentrader.org
//   |    http://opentrader.codeplex.com
//   |
//   |  For professional services, please visit us at :
//   |    http://www.systemathics.com
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Org.OpenTrader.Framework.Forge
{
    #region

    using System;
    using System.ComponentModel;

    using Org.OpenTrader.Framework.DataModel;
    using Org.OpenTrader.Framework.Events;
    using Org.OpenTrader.Framework.Forge.DataCache;
    using Org.OpenTrader.Framework.Forge.DataModel;
    using Org.OpenTrader.Framework.Forge.Enums;
    using Org.OpenTrader.Framework.Forge.Interfaces;
    using Org.OpenTrader.Framework.LiveObjects;
    using Org.OpenTrader.Framework.LiveObjects.Interfaces;

    #endregion

    /// <summary>
    /// The instrument. TODO paly with this for underlying instruments (options) [TypeDescriptionProvider(typeof(InstrumentTypeDescriptionProvider))]
    /// </summary>
    [Serializable]
    public abstract class InstrumentStaticData : IInstrumentStaticData
    {
        #region Constants and Fields

        /// <summary>
        /// The black listed.
        /// </summary>
        private bool blackListed;

        /// <summary>
        /// The buy enabled.
        /// </summary>
        private bool buyEnabled;

        /// <summary>
        /// The code.
        /// </summary>
        private string code;

        /// <summary>
        /// The currency.
        /// </summary>
        private ECurrency currency;

        /// <summary>
        /// The enabled.
        /// </summary>
        private bool enabled;

        /// <summary>
        /// The feedWiring.
        /// </summary>
        private IFeedWiring feedWiring;

        /// <summary>
        /// The id.
        /// </summary>
        private Guid id;

        /// <summary>
        /// The isin.
        /// </summary>
        private string isin;

        /// <summary>
        /// The lot size.
        /// </summary>
        private int lotSize;

        /// <summary>
        /// The market.
        /// </summary>
        private IMarket market;

        /// <summary>
        /// The max nominal book.
        /// </summary>
        private double maxNominalBook;

        /// <summary>
        /// The max nominal trade.
        /// </summary>
        private double maxNominalTrade;

        /// <summary>
        /// The max variation percentage.
        /// </summary>
        private double maxVariationPercentage;

        /// <summary>
        /// The name.
        /// </summary>
        private string name;

        /// <summary>
        /// The orderWiring.
        /// </summary>
        private IOrderWiring orderWiring;

        /// <summary>
        /// The point value.
        /// </summary>
        private double pointValue;

        /// <summary>
        /// The restricted.
        /// </summary>
        private bool restricted;

        /// <summary>
        /// The sell enabled.
        /// </summary>
        private bool sellEnabled;

        /// <summary>
        /// The short sell enabled.
        /// </summary>
        private bool shortSellEnabled;

        /// <summary>
        /// The tick size rule.
        /// </summary>
        private ITickSizeRule tickSizeRule;

        /// <summary>
        /// The trading scope.
        /// </summary>
        private ETradingScope tradingScope;

        #endregion

        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="InstrumentStaticData"/> class. 
        /// Initializes a new instance of the <see cref="Instrument"/> class.
        /// </summary>
        /// <param name="dictionaryID">
        /// The dictionary ID.
        /// </param>
        /// <param name="dbInstrument">
        /// The dbInstrument.
        /// </param>
        public InstrumentStaticData(Guid dictionaryID, DBInstrumentStaticData dbInstrument)
        {
            // Belongs to this dictionary
            this.DictionaryID = dictionaryID;

            // Unique identifier of this instrument
            this.ID = dbInstrument.ID;

            // Code
            this.Code = dbInstrument.Code;

            // Name of this instrument
            this.Name = dbInstrument.Name;

            // ISIN Code
            this.ISIN = dbInstrument.ISIN;

            // Currency
            this.Currency = EnumDecoder<ECurrency>.Parse(dbInstrument.Currency);

            // Tick Size Rule
            this.TickSizeRule = Singleton<DC>.Instance.LoadTSR(dbInstrument.DBTickSizeRuleID);

            // InstrumentStaticData Security
            this.Enabled = dbInstrument.Enabled;
            this.BuyEnabled = dbInstrument.BuyEnabled;
            this.SellEnabled = dbInstrument.SellEnabled;
            this.ShortSellEnabled = dbInstrument.ShortSellEnabled;
            this.BlackListed = dbInstrument.BlackListed;
            this.HardToBorrow = dbInstrument.HardToBorrow;
            this.Restricted = dbInstrument.Restricted;
            this.MaxNominalTrade = dbInstrument.MaxNominalTrade;
            this.MaxNominalBook = dbInstrument.MaxNominalBook;
            this.MaxVariationPercentage = dbInstrument.MaxVariationPercentage;

            // Point Value
            if (dbInstrument.PointValue.HasValue)
            {
                this.PointValue = dbInstrument.PointValue.Value;
            }
            else
            {
                this.PointValue = 1.0;
            }

            // LotSize
            if (dbInstrument.LotSize.HasValue)
            {
                this.LotSize = dbInstrument.LotSize.Value;
            }
            else
            {
                this.LotSize = 1;
            }

            // Trading scope
            this.TradingScope = EnumDecoder<ETradingScope>.Parse(dbInstrument.TradingScope);

            // Market this instrument belongs to
            dbInstrument.DBMarketReference.Load();
            this.Market = new Market(dbInstrument.DBMarket);

            // Current container
            var container =
                Singleton<LiveObjectsRegistry>.Instance.GetContainer(
                    Singleton<LiveObjectsRegistry>.Instance.CurrentLiveObjectContainer.Descriptor.ID);

            // Wiring for feed
            if (dbInstrument.DBFeedPluginID != null)
            {
                var layer =
                    Singleton<LiveObjectsRegistry>.Instance.GetLiveObject((Guid)dbInstrument.DBFeedPluginID) as
                    IFeedPlugin;
                if (layer != null)
                {
                    IConfiguration subscriptionConfiguration = null;
                    if (dbInstrument.DBFeedPluginInstrumentStaticDataConfigurationID.HasValue)
                    {
                        subscriptionConfiguration =
                            Singleton<DC>.Instance.LoadConfiguration(
                                (Guid)dbInstrument.DBFeedPluginInstrumentStaticDataConfigurationID, 
                                (layer.Descriptor as LiveObjectDescriptor.Local).ClassType, 
                                layer.SubscriptionConfigurationSchema, 
                                container);
                    }

                    this.feedWiring = new FeedWiring(
                        dbInstrument.ID, (Guid)dbInstrument.DBFeedPluginID, subscriptionConfiguration);
                }
                else
                {
                    // Something is really wrong, just discard any feed wiring for this instrument
                    DBConfigurationHelpers.DeleteDBConfiguration(
                        (Guid)dbInstrument.DBFeedPluginInstrumentStaticDataConfigurationID);
                    dbInstrument.DBFeedPluginID = null;
                    dbInstrument.DBFeedPluginInstrumentStaticDataConfigurationID = null;
                    var n = Singleton<DBForge>.Instance.Commit();
                }
            }

            // Wiring for order
            if (dbInstrument.DBOrderPluginID != null)
            {
                var layer =
                    Singleton<LiveObjectsRegistry>.Instance.GetLiveObject((Guid)dbInstrument.DBOrderPluginID) as
                    IOrderPlugin;
                if (layer != null)
                {
                    IConfiguration orderRoutingConfiguration = null;
                    if (dbInstrument.DBOrderPluginInstrumentStaticDataConfigurationID.HasValue)
                    {
                        orderRoutingConfiguration =
                            Singleton<DC>.Instance.LoadConfiguration(
                                (Guid)dbInstrument.DBFeedPluginInstrumentStaticDataConfigurationID, 
                                (layer.Descriptor as LiveObjectDescriptor.Local).ClassType, 
                                layer.OrderRoutingConfigurationSchema, 
                                container);
                    }

                    this.orderWiring = new OrderWiring(
                        dbInstrument.ID, (Guid)dbInstrument.DBOrderPluginID, orderRoutingConfiguration);
                }
                else
                {
                    // Something is really wrong, just discard any order wiring for this instrument
                    DBConfigurationHelpers.DeleteDBConfiguration(
                        (Guid)dbInstrument.DBOrderPluginInstrumentStaticDataConfigurationID);
                    dbInstrument.DBOrderPluginID = null;
                    dbInstrument.DBOrderPluginInstrumentStaticDataConfigurationID = null;
                    var n = Singleton<DBForge>.Instance.Commit();
                }
            }
        }

        #endregion

        #region Events

        /// <summary>
        /// The property event cb.
        /// </summary>
        public event EventHandler<PropertyEventArgs> PropertyEventCB;

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets a value indicating whether BlackListed.
        /// </summary>
        [Category("Instrument Security")]
        public bool BlackListed
        {
            get
            {
                return this.blackListed;
            }

            set
            {
                var old = this.blackListed;
                this.blackListed = value;
                this.RaisePropertyChanged("BlackListed", old, value);
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether BuyEnabled.
        /// </summary>
        [Category("Instrument Security")]
        public bool BuyEnabled
        {
            get
            {
                return this.buyEnabled;
            }

            set
            {
                var old = this.buyEnabled;
                this.buyEnabled = value;
                this.RaisePropertyChanged("BuyEnabled", old, value);
            }
        }

        /// <summary>
        /// Gets or sets Code.
        /// </summary>
        [Category("Instrument")]
        public string Code
        {
            get
            {
                return this.code;
            }

            set
            {
                var old = this.code;
                this.code = value;
                this.RaisePropertyChanged("Code", old, value);
            }
        }

        /// <summary>
        /// Gets or sets the natural currency of the instrument
        /// </summary>
        [Category("Instrument")]
        public ECurrency Currency
        {
            get
            {
                return this.currency;
            }

            set
            {
                var old = this.currency;
                this.currency = value;
                this.RaisePropertyChanged("Currency", old, value);
            }
        }

        /// <summary>
        /// Gets or sets CurrencyPrice.
        /// </summary>
        [Category("Instrument")]
        [ReadOnly(true)]
        public double? CurrencyPrice
        {
            get
            {
                var currencies = Singleton<DC>.Instance.GetCurrencyPrices(this.DictionaryID);

                if (currencies.ContainsKey(this.Currency))
                {
                    return currencies[this.Currency];
                }

                return new double?();
            }
        }

        /// <summary>
        /// Gets DictionaryCurrency.
        /// </summary>
        [Category("Instrument")]
        [ReadOnly(true)]
        [Browsable(false)]
        public ECurrency DictionaryCurrency
        {
            get
            {
                return Singleton<DC>.Instance.GetDomesticCurrency(this.DictionaryID);
            }
        }

        /// <summary>
        /// Gets DictionaryID.
        /// </summary>
        [Browsable(false)]
        public Guid DictionaryID { get; private set; }

        /// <summary>
        /// Gets or sets a value indicating whether Enabled.
        /// </summary>
        [Category("Instrument Security")]
        public bool Enabled
        {
            get
            {
                return this.enabled;
            }

            set
            {
                var old = this.enabled;
                this.enabled = value;
                this.RaisePropertyChanged("Enabled", old, value);
            }
        }

        /// <summary>
        /// Gets or sets FactorPrice.
        /// </summary>
        [Category("Instrument")]
        [ReadOnly(true)]
        public double? FactorPrice
        {
            get
            {
                if (! this.CurrencyPrice.HasValue)
                {
                    return new double?();
                }

                return this.PointValue * this.CurrencyPrice.Value;
            }
        }

        /// <summary>
        /// Gets or sets FeedWiring.
        /// </summary>
        [Category("Instrument Wirings")]
        public IFeedWiring FeedWiring
        {
            get
            {
                return this.feedWiring;
            }

            set
            {
                // Remove previous configuration if any
                if (value != this.feedWiring && this.feedWiring != null)
                {
                    Singleton<DC>.Instance.DeleteConfiguration(this.feedWiring.SubscriptionConfiguration.ID);
                }

                // Old value
                var old = this.feedWiring;

                // New value
                this.feedWiring = value;
                if (this.feedWiring != null)
                {
                    (this.feedWiring as FeedWiring).InstrumentID = this.ID;
                }

                // Save
                Singleton<DC>.Instance.SaveInstrumentStaticDataWirings(this);

                this.RaisePropertyChanged("FeedWiring", old, value);
            }
        }

        /// <summary>
        /// Gets FriendlyName.
        /// </summary>
        [Browsable(false)]
        public virtual string FriendlyName
        {
            get
            {
                return string.Format("{0}/{1} [{2}]", this.Code, this.TradingScope, this.ID);
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether HardToBorrow.
        /// </summary>
        [Category("Instrument Security")]
        public bool HardToBorrow { get; set; }

        /// <summary>
        /// Gets or sets ID.
        /// </summary>
        [Category("Instrument")]
        [ReadOnly(true)]
        [Browsable(false)]
        public Guid ID
        {
            get
            {
                return this.id;
            }

            set
            {
                var old = this.id;
                this.id = value;
                this.RaisePropertyChanged("ID", old, value);
            }
        }

        /// <summary>
        /// Gets or sets ISIN.
        /// </summary>
        [Category("Instrument")]
        public string ISIN
        {
            get
            {
                return this.isin;
            }

            set
            {
                var old = this.isin;
                this.isin = value;
                this.RaisePropertyChanged("ISIN", old, value);
            }
        }

        /// <summary>
        /// Gets or sets LotSize.
        /// </summary>
        [Category("Instrument")]
        [ReadOnly(true)]
        public int LotSize
        {
            get
            {
                return this.lotSize;
            }

            set
            {
                var old = this.lotSize;
                this.lotSize = value;
                this.RaisePropertyChanged("LotSize", old, value);
            }
        }

        /// <summary>
        /// Gets or sets Market.
        /// </summary>
        [Category("Instrument")]
        [ReadOnly(true)]
        [Browsable(false)]
        public IMarket Market
        {
            get
            {
                return this.market;
            }

            set
            {
                var old = this.market;
                this.market = value;
                this.RaisePropertyChanged("Market", old, value);
            }
        }

        /// <summary>
        /// Gets or sets MaxNominalBook.
        /// </summary>
        [Category("Instrument Security")]
        public double MaxNominalBook
        {
            get
            {
                return this.maxNominalBook;
            }

            set
            {
                var old = this.maxNominalBook;
                this.maxNominalBook = value;
                this.RaisePropertyChanged("MaxNominalTrade", old, value);
            }
        }

        /// <summary>
        /// Gets or sets MaxNominalTrade.
        /// </summary>
        [Category("Instrument Security")]
        public double MaxNominalTrade
        {
            get
            {
                return this.maxNominalTrade;
            }

            set
            {
                var old = this.maxNominalTrade;
                this.maxNominalTrade = value;
                this.RaisePropertyChanged("MaxNominalTrade", old, value);
            }
        }

        /// <summary>
        /// Gets or sets MaxVariationPercentage.
        /// </summary>
        [Category("Instrument Security")]
        public double MaxVariationPercentage
        {
            get
            {
                return this.maxVariationPercentage;
            }

            set
            {
                var old = this.maxVariationPercentage;
                this.maxVariationPercentage = value;
                this.RaisePropertyChanged("MaxVariationPercentage", old, value);
            }
        }

        /// <summary>
        /// Gets or sets Name.
        /// </summary>
        [Category("Instrument")]
        public string Name
        {
            get
            {
                return this.name;
            }

            set
            {
                var old = this.name;
                this.name = value;
                this.RaisePropertyChanged("Name", old, value);
            }
        }

        /// <summary>
        /// Gets or sets OrderWiring.
        /// </summary>
        [Category("Instrument Wirings")]
        public IOrderWiring OrderWiring
        {
            get
            {
                return this.orderWiring;
            }

            set
            {
                // Remove previous configuration if any
                if (value != this.orderWiring && this.orderWiring != null)
                {
                    Singleton<DC>.Instance.DeleteConfiguration(this.orderWiring.OrderRoutingConfiguration.ID);
                }

                // Old value
                var old = this.orderWiring;

                // New value
                this.orderWiring = value;
                if (this.orderWiring != null)
                {
                    (this.orderWiring as OrderWiring).InstrumentID = this.ID;
                }

                // Save
                Singleton<DC>.Instance.SaveInstrumentStaticDataWirings(this);

                this.RaisePropertyChanged("OrderWiring", old, value);
            }
        }

        /// <summary>
        /// Gets or sets PointValue.
        /// </summary>
        [Category("Instrument")]
        [ReadOnly(true)]
        public double PointValue
        {
            get
            {
                return this.pointValue;
            }

            set
            {
                var old = this.pointValue;
                this.pointValue = value;
                this.RaisePropertyChanged("PointValue", old, value);
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether Restricted.
        /// </summary>
        [Category("Instrument Security")]
        public bool Restricted
        {
            get
            {
                return this.restricted;
            }

            set
            {
                var old = this.restricted;
                this.restricted = value;
                this.RaisePropertyChanged("Restricted", old, value);
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether SellEnabled.
        /// </summary>
        [Category("Instrument Security")]
        public bool SellEnabled
        {
            get
            {
                return this.sellEnabled;
            }

            set
            {
                var old = this.sellEnabled;
                this.sellEnabled = value;
                this.RaisePropertyChanged("SellEnabled", old, value);
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether ShortSellEnabled.
        /// </summary>
        [Category("Instrument Security")]
        public bool ShortSellEnabled
        {
            get
            {
                return this.shortSellEnabled;
            }

            set
            {
                var old = this.shortSellEnabled;
                this.shortSellEnabled = value;
                this.RaisePropertyChanged("ShortSellEnabled", old, value);
            }
        }

        /// <summary>
        /// Gets or sets TickSizeRule.
        /// </summary>
        [Category("Instrument")]
        [ReadOnly(true)]
        [Browsable(false)]
        public ITickSizeRule TickSizeRule
        {
            get
            {
                return this.tickSizeRule;
            }

            set
            {
                var old = this.tickSizeRule;
                this.tickSizeRule = value;
                this.RaisePropertyChanged("TickSizeRule", old, value);
            }
        }

        /// <summary>
        /// Gets or sets TradingScope.
        /// </summary>
        [Category("Instrument")]
        [ReadOnly(true)]
        [Browsable(false)]
        public ETradingScope TradingScope
        {
            get
            {
                return this.tradingScope;
            }

            set
            {
                var old = this.tradingScope;
                this.tradingScope = value;
                this.RaisePropertyChanged("TradingScope", old, value);
            }
        }

        /// <summary>
        /// Gets Type.
        /// </summary>
        [Category("Instrument")]
        [ReadOnly(true)]
        [Browsable(false)]
        public abstract string Type { get; }

        #endregion

        #region Public Methods

        /// <summary>
        /// The dispose.
        /// </summary>
        public void Dispose()
        {
        }

        /// <summary>
        /// Force the derived class to define this
        /// </summary>
        /// <returns>
        /// A human readable representation of the static data type
        /// </returns>
        public new virtual string ToString()
        {
            return this.FriendlyName;
        }

        #endregion

        #region Methods

        /// <summary>
        /// The fire property changed.
        /// </summary>
        /// <param name="fieldName">
        /// The field name.
        /// </param>
        /// <param name="oldValue">
        /// The old value.
        /// </param>
        /// <param name="newValue">
        /// The new value.
        /// </param>
        private void RaisePropertyChanged(string fieldName, object oldValue, object newValue)
        {
            if (this.PropertyEventCB != null)
            {
                this.PropertyEventCB(this, new PropertyEventArgs.Changed(fieldName, oldValue, newValue));
            }
        }

        #endregion
    }
}